#!/usr/bin/env python
##
#  Project: Kelindo - Unsupervised Music Organizer  
#  Author: Nicola Bicocchi <nicola.bicocchi@gmail.com>, Nicolo' Torreggiani <nicolo.torreggiani@gmail.com>, Gianluca Puglia <gianluca.puglia@gmail.com>, Matteo Senardi <pualien@gmail.com>
#  Copyright: 2013 Nicola Bicocchi, Nicolo' Torreggiani, Gianluca Puglia, Matteo Senardi
#  License: GPL-2+
#  This program is free software; you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the Free
#  Software Foundation; either version 2 of the License, or (at your option)
#  any later version.
# 
#  This program is distributed in the hope that it will be useful, but WITHOUT
#  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
#  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
#  more details.
# 
# On Debian GNU/Linux systems, the full text of the GNU General Public License
# can be found in the file /usr/share/common-licenses/GPL-2.
##

import time
import requests
from xml.dom import minidom

class MBError(Exception): pass

class MusicBrainz:
    def __init__(self):
        self.speed_limit_pause = 10
        self.max_attempts = 3
        return

    def __xml_retrieve(self, query_string):
        """ Handles MB speed limits """
        attempt = 0
        while attempt <= self.max_attempts:
            attempt += 1
            # raises connectionerror; still not managed
            r = requests.get(query_string)
            if r.status_code == 503:
                time.sleep(self.speed_limit_pause)            
            elif r.status_code == 200:
                return r.content
        return
        
        
    def __split_by_medium(self, tracks):        
        """ Splits albums with multiple mediums """
        l = [tracks, ]
        
        mediums = set()
        for track in tracks:
            mediums.add(track['trackmedium'])
        if len(mediums) == 1:
            return l

        for medium in mediums:
            l.append(filter(lambda t: t['trackmedium'] == medium, tracks))
        return l
            
    
    def get_MBID(self, durations):
        """ Given a list of durations, returns a list of MBIDs """
        outList = []
        
        # first track ID
        outList.append('1')
        # number of tracks
        outList.append(str(len(durations)))

        # standard audio cd offset (2 seconds, 150 LBA)
        offset = 150
        for d in durations:
            outList.append(str(offset))
            # starting point of each track
            offset += d * 75
            
        # total duration (as third parameter)
        outList.insert(2, str(offset))

        queryStr = 'http://musicbrainz.org/ws/2/discid/nullnullnullnullnullnullnul-?toc=' + '+'.join(outList)
        xml = self.__xml_retrieve(queryStr)
        dom = minidom.parseString(xml)
            
        MBID = []
        for release in dom.getElementsByTagName("release"):
            MBID.append(release.getAttribute('id'))
        return MBID
        

    def get_albums(self, MBID):
        queryStr = 'http://musicbrainz.org/ws/2/release/%s?inc=artist-credits+labels+discids+recordings' % (MBID,)
        dom = minidom.parseString(self.__xml_retrieve(queryStr))

        # album release year
        albumYear = ''
        try:
            albumYear = dom.getElementsByTagName('date')[0].firstChild.nodeValue.split('-')[0]        
        except IndexError:
            # non fatal error
            pass        
        
        try:
            # album authors and title
            albumTitle = dom.getElementsByTagName('title')[0].firstChild.nodeValue
            albumAuthors = []
            for author in dom.getElementsByTagName("artist-credit")[0].getElementsByTagName("name"):
                albumAuthors.append(author.firstChild.nodeValue)
            albumAuthors = ', '.join(albumAuthors)

            # track-related data
            tracks = []
            for medium in dom.getElementsByTagName("medium"):
                trackMedium = medium.getElementsByTagName('position')[0].firstChild.nodeValue
                for track in medium.getElementsByTagName("track"):
                    trackTitle = track.getElementsByTagName('title')[0].firstChild.nodeValue
                    #Some songs have / in the title (so bad for the encoder)
                    trackTitle = trackTitle.replace('/', '-')
                    trackPosition = track.getElementsByTagName('position')[0].firstChild.nodeValue
                    trackAuthors = []
                    for author in track.getElementsByTagName("artist-credit")[0].getElementsByTagName("name"):
                        trackAuthors.append(author.firstChild.nodeValue)
                    trackAuthors = ', '.join(trackAuthors)
                        
                    tracks.append({
                        'albumtitle' : albumTitle,
                        'albumauthors' : albumAuthors,
                        'albumyear' : albumYear,
                        'tracktitle' : trackTitle,
                        'trackauthors' : trackAuthors,
                        'trackmedium' : trackMedium,
                        'trackposition' : trackPosition,
                    })
        except IndexError:
            raise MBError
                
                #print authors, album, year, mediumNumber, position, title.encode('utf-8'), tauthors
              
        return self.__split_by_medium(tracks)
            
